#include <iostream>
#include <fstream>
#include "q1.h"

using namespace std;

// constructor
Q1::Q1(list<Structures::process> p){
	processes = p;
	processes_BL = p;

	Structures::processor p1('A', 8388608, 2000);
	Structures::processor p2('B', 8388608, 2000);
	Structures::processor p3('C', 8388608, 2000);
	Structures::processor p4('D', 8388608, 2000);
	Structures::processor p5('E', 8388608, 2000);

	Structures::processor p1_BL('A', 8388608, 2000);
	Structures::processor p2_BL('B', 8388608, 2000);
	Structures::processor p3_BL('C', 8388608, 2000);
	Structures::processor p4_BL('D', 8388608, 2000);
	Structures::processor p5_BL('E', 8388608, 2000);

	p1.cyclesRemaining = 0; p2.cyclesRemaining = 0; p3.cyclesRemaining = 0; p4.cyclesRemaining = 0; p5.cyclesRemaining = 0;	
	p1_BL.cyclesRemaining = 0; p2_BL.cyclesRemaining = 0; p3_BL.cyclesRemaining = 0; p4_BL.cyclesRemaining = 0; p5_BL.cyclesRemaining = 0;

	processors.push_back(p1);
	processors.push_back(p2);
	processors.push_back(p3);
	processors.push_back(p4);
	processors.push_back(p5);

	processors_BL.push_back(p1_BL);
	processors_BL.push_back(p2_BL);
	processors_BL.push_back(p3_BL);
	processors_BL.push_back(p4_BL);
	processors_BL.push_back(p5_BL);
}

// generate and schedule processes
void Q1::schedule(){
	processes.sort(Structures::compareProcessCycles);

	while(processes.size() != 0){
		// get min process
		list<Structures::process>::iterator i = processes.begin();
		Structures::process minProcess = (*i);
		processes.pop_front();

		// get min processor
		processors.sort(Structures::compareProcessorCycles);
		list<Structures::processor>::iterator j = processors.begin();
		(*j).processes.push_back(minProcess);
		(*j).cyclesRemaining += minProcess.burst;
	}
}

// print list
void Q1::print(){
	fstream outfile;
	outfile.open("question1_output.txt", fstream::out | fstream::app);

	double maxRunTime = 0;
	double avgWaitTime = 0;
	long double avgCycleCount = 0;
	double cycleStdDeviation = 0;
	long cycleCounts[5] = {0, 0, 0, 0, 0};

	// calculate metrics for each processor
	for(int i = 0; i < 5; i++){

		int num = 0;
		double totalTime = 0.0;
		double waitTime = 0.0;

		// add total time, cycles, and wait time for each process in the current processor's queue
		for(list<Structures::process>::iterator currProcess = (*processors.begin()).processes.begin(); currProcess != (*processors.begin()).processes.end(); currProcess++)
		{
			//metrics
			totalTime += (*currProcess).burst / (double)(*processors.begin()).speed;
			avgCycleCount += (*currProcess).burst;
			cycleCounts[i] += (*currProcess).burst;
			waitTime += totalTime;
			num++;
		}
		waitTime -= totalTime; // remove the last process's time

		// find the processor with the maximum run time
		if(totalTime > maxRunTime)
			maxRunTime = totalTime;

		// Avg wait time: divide the summation of waiting times by num
		avgWaitTime += waitTime / (double)num;
		
		// move on to next processor
		processors.pop_front();
	}

	// calculate the average cycle count for all processors
	avgCycleCount = avgCycleCount/(long double)5;

	// calculate the standard deviation for the cycle counts
	cycleStdDeviation = sqrt( (long double)( pow((long double)(cycleCounts[0] - avgCycleCount), 2) + pow((long double)(cycleCounts[1] - avgCycleCount), 2) + pow((long double)(cycleCounts[2] - avgCycleCount), 2) + pow((long double)(cycleCounts[3] - avgCycleCount), 2) + pow((long double)(cycleCounts[4] - avgCycleCount), 2) ) / (long double)5.0 );

	// write to output file
	outfile << maxRunTime << "," << avgWaitTime/5.0 << "," << cycleStdDeviation << endl;
	outfile.close();
}

// calculate baseline
void Q1::baseline(){
	// open file for writing
	fstream baselineOutfile;
	baselineOutfile.open ("baseline1_output.txt", fstream::out | fstream::app);

	// FIFO process scheduling, cycling through processors, for baseline
	while(processes_BL.size() != 0){

		// for all processors
		for(list<Structures::processor>::iterator currProcessor = processors_BL.begin(); currProcessor != processors_BL.end(); currProcessor++)
		{
			// get next process
			list<Structures::process>::iterator currProcess = processes_BL.begin();
			Structures::process minProcess = (*currProcess);
			processes_BL.pop_front();

			(*currProcessor).processes.push_back(minProcess);
			(*currProcessor).cyclesRemaining += minProcess.burst;
		}
	}

	double maxRunTime = 0;
	double avgWaitTime = 0;
	long double avgCycleCount = 0;
	double cycleStdDeviation = 0;
	long cycleCounts[5] = {0, 0, 0, 0, 0};

	// calculate metrics for each processor
	for(int i = 0; i < 5; i++){

		int num = 0;
		double totalTime = 0.0;
		double waitTime = 0.0;

		// add total time, cycles, and wait time for each process in the current processor's queue
		for(list<Structures::process>::iterator currProcess = (*processors_BL.begin()).processes.begin(); currProcess != (*processors_BL.begin()).processes.end(); currProcess++)
		{
			//metrics
			totalTime += (*currProcess).burst / (double)(*processors_BL.begin()).speed;
			avgCycleCount += (*currProcess).burst;
			cycleCounts[i] += (*currProcess).burst;
			waitTime += totalTime;
			num++;
		}
		waitTime -= totalTime; // remove the last process's time

		// find the processor with the maximum run time
		if(totalTime > maxRunTime)
			maxRunTime = totalTime;

		// Avg wait time: divide the summation of waiting times by num
		avgWaitTime += waitTime / (double)num;
		
		// move on to next processor
		processors_BL.pop_front();
	}

	// calculate the average cycle count for all processors
	avgCycleCount = avgCycleCount/(long double)5;

	// calculate the standard deviation for the cycle counts
	cycleStdDeviation = sqrt( (long double)( pow((long double)(cycleCounts[0] - avgCycleCount), 2) + pow((long double)(cycleCounts[1] - avgCycleCount), 2) + pow((long double)(cycleCounts[2] - avgCycleCount), 2) + pow((long double)(cycleCounts[3] - avgCycleCount), 2) + pow((long double)(cycleCounts[4] - avgCycleCount), 2) ) / (long double)5.0 );

	// write to output file
	baselineOutfile << maxRunTime << "," << avgWaitTime/5.0 << "," << cycleStdDeviation << endl;
	baselineOutfile.close();
}